﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace theguineasyndrome
{
    public class EditorMode : AppMode
    {
        private editor.forms.EditorForm editorForm;
        private editor.forms.MarkNameForm markNameForm;

        private LevelElement currentElement;
        private LevelElement alignElement;
        private editor.PointMark nextPointMark;

        private Texture2D pointMarkTexture;
        private SpriteFont pointMarkFont;

        private delegate void EditFunction();
        private Dictionary<string, EditFunction> editActions;

        public EditorMode(TheGuineaSyndromeGame game, GraphicsDevice graphicsDevice, ContentManager Content, GameWindow Window)
            : base(game, graphicsDevice, Content, Window)
        {
            nextPointMark = null;
            editActions = new Dictionary<string, EditFunction>();
            editActions["Objects"] = editObjects;
            editActions["Marks"] = editMarks;
            editActions["Load & Save"] = null;
        }

        public override bool isFullScreen()
        {
            return false;
        }

        public override void Initialize()
        {
            base.Initialize();

            Level level = new Level(content);
            level.paginateElements(new List<LevelElement>());
            setLevel(level);

            editorForm = new editor.forms.EditorForm(content, this);
            editorForm.Show();

            markNameForm = new editor.forms.MarkNameForm(this);
        }

        public override void LoadContent()
        {
            base.LoadContent();

            pointMarkTexture = content.Load<Texture2D>("images/redsquare");
            pointMarkFont = content.Load<SpriteFont>("fonts/editor");
        }

        public override void UnloadContent()
        {
            base.UnloadContent();
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            Vector2 cameraMove = new Vector2();

            if (isPressed(Keys.Left))
                cameraMove.X--;

            if (isPressed(Keys.Right))
                cameraMove.X++;

            if (isPressed(Keys.Up))
                cameraMove.Y--;

            if (isPressed(Keys.Down))
                cameraMove.Y++;

            cameraMove *= gameTime.ElapsedGameTime.Milliseconds * 1.5f;
            camera.moveBy(cameraMove);

            currentElement = null;

            EditFunction edit = editActions[editorForm.getTabName()];
            if (edit != null)
                edit();
        }

        private void editObjects()
        {
            Vector2 pointer = getPointer();

            Vector2 mouse = camera.getRelative(pointer);

            if (isJustPressed(Keys.LeftShift) || isJustPressed(Keys.RightShift))
            {
                alignElement = level.getElementAt(mouse, editorForm.displayBackground(), editorForm.displayObstacles(), editorForm.displayForeground());
                if (alignElement != null)
                    alignElement.setColor(Color.Red);
            }
            else if (isJustReleased(Keys.LeftShift) || isJustReleased(Keys.RightShift))
            {
                if (alignElement != null)
                {
                    alignElement.setColor(Color.White);
                    alignElement = null;
                }
            }

            Texture2D elementTexture = editorForm.getCurrentTexture();
            Vector2 elementPosition = new Vector2(mouse.X - elementTexture.Width / 2, mouse.Y - elementTexture.Height / 2);
            currentElement = editorForm.getNewElement(elementPosition);

            if (alignElement != null)
            {
                if (isJustPressed("RightButton"))
                {
                    level.removeElement(alignElement);
                    alignElement = null;
                }
                else
                {
                    const float margin = 25;

                    // if both elements are obstacles then glue to the collision borders
                    if (currentElement is ObstacleElement && alignElement is ObstacleElement)
                    {
                        ObstacleElement currentElementAsObstacle = (ObstacleElement)currentElement;
                        ObstacleElement alignElementAsObstacle = (ObstacleElement)alignElement;

                        if (Math.Abs(currentElementAsObstacle.CollisionBottom - alignElementAsObstacle.CollisionTop) < margin)
                            currentElementAsObstacle.Bottom = alignElementAsObstacle.CollisionTop + ObstacleElement.HALF_DEPTH;

                        else if (Math.Abs(currentElementAsObstacle.CollisionTop - alignElementAsObstacle.CollisionTop) < margin)
                            currentElementAsObstacle.Top = alignElementAsObstacle.CollisionTop - ObstacleElement.HALF_DEPTH;

                        else if (Math.Abs(currentElementAsObstacle.CollisionBottom - alignElementAsObstacle.CollisionBottom) < margin)
                            currentElementAsObstacle.Bottom = alignElementAsObstacle.CollisionBottom + ObstacleElement.HALF_DEPTH;

                        else if (Math.Abs(currentElementAsObstacle.CollisionTop - alignElementAsObstacle.CollisionBottom) < margin)
                            currentElementAsObstacle.Top = alignElementAsObstacle.CollisionBottom - ObstacleElement.HALF_DEPTH;
                    }
                    else
                    {
                        if (Math.Abs(currentElement.Bottom - alignElement.Top) < margin)
                            currentElement.Bottom = alignElement.Top;

                        else if (Math.Abs(currentElement.Top - alignElement.Top) < margin)
                            currentElement.Top = alignElement.Top;

                        else if (Math.Abs(currentElement.Bottom - alignElement.Bottom) < margin)
                            currentElement.Bottom = alignElement.Bottom;

                        else if (Math.Abs(currentElement.Top - alignElement.Bottom) < margin)
                            currentElement.Top = alignElement.Bottom;
                    }

                    if (Math.Abs(currentElement.Right - alignElement.Left) < margin)
                        currentElement.Right = alignElement.Left;

                    else if (Math.Abs(currentElement.Left - alignElement.Left) < margin)
                        currentElement.Left = alignElement.Left;

                    else if (Math.Abs(currentElement.Right - alignElement.Right) < margin)
                        currentElement.Right = alignElement.Right;

                    else if (Math.Abs(currentElement.Left - alignElement.Right) < margin)
                        currentElement.Left = alignElement.Right;
                }
            }

            if (isJustPressed("LeftButton") && camera.isInside(pointer))
                level.addElement(currentElement);
        }

        private void editMarks()
        {
            Vector2 pointer = getPointer();
            Vector2 mouse = camera.getRelative(pointer);
            if (isJustPressed("LeftButton") && camera.isInside(pointer) && !markNameForm.Visible)
            {
                nextPointMark = new editor.PointMark(pointMarkTexture, pointMarkFont, mouse);
                markNameForm.ShowDialog();
            }
        }

        public void setMarkName(string markName)
        {
            nextPointMark.setName(markName);
            level.addPointMark(nextPointMark);
            nextPointMark = null;
        }

        public override void Draw(GameTime gameTime)
        {
            level.prepareDrawing(camera);

            if (editorForm.getTabName() == "Objects")
            {
                if (currentElement != null)
                {
                    currentElement.queueDrawing(camera);
                }
            }

            spriteBatch.Begin();

            // background
            if (editorForm.displayBackground())
                level.drawBackgrounds(spriteBatch, camera);

            // level
            if (editorForm.displayObstacles())
                level.drawObjects(spriteBatch, camera);

            // foreground
            if (editorForm.displayForeground())
                level.drawForegrounds(spriteBatch, camera);

            Dictionary<string, editor.PointMark> pointMarks = level.getPointsMarks();
            foreach (KeyValuePair<string, editor.PointMark> mark in pointMarks)
            {
                mark.Value.Draw(spriteBatch, camera);
            }

            if (editorForm.getTabName() == "Marks")
            {
                Vector2 pointMarkTextureHalfSize = new Vector2(pointMarkTexture.Width, pointMarkTexture.Height) / 2;

                Vector2 mouse = camera.getRelative(getPointer());
                new editor.PointMark(pointMarkTexture, pointMarkFont, mouse).Draw(spriteBatch, camera);

                if (nextPointMark != null)
                    nextPointMark.Draw(spriteBatch, camera);
            }

            spriteBatch.End();

            base.Draw(gameTime);
        }

        public void removeNextPointMark()
        {
            nextPointMark = null;
        }
    }
}
